#include "GKStates.h"
#include "Ball.h"
#include "GoalKeeper.h"
#include "Messages.h"
#include "Team.h"
#include "Telegram.h"
#include "Utils.h"
#include "TelegramDispatcher.h"


/*	GKGlobalState
	It's Singleton, to call it: GKGlobalState::Istance();
	This is the always-on state for every GoalKeeper current in game.
*/

void GKGlobalState::Enter(GoalKeeper* gk)
{

}

void GKGlobalState::Execute(GoalKeeper* gk)
{
	
}

void GKGlobalState::Exit(GoalKeeper* gk)
{

}

bool GKGlobalState::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	switch (telegram.Msg)
	{
	case BallIncoming:
		if(gk->GetTeam()->IsUserControlled()) gk->GetTeam()->SetPlayerUserControlled(gk);
		else gk->GetFSM()->ChangeState(GKReceivePass::Istance());
		return true;

	case PrepareKickOff:
		gk->GetFSM()->ChangeState(GKKickOff::Istance());
		return true;

	default:
		break;
	}

	return false;
}

/*	GKHoldBall
	It's Singleton, to call it: GKHoldBall::Istance();
	Gk has the ball. Come back to home if there isn't a possible pass.
*/

void GKHoldBall::Enter(GoalKeeper* gk)
{
	gk->Steering()->SeekOn();

	gk->SetTarget(gk->GetHomeRegion()->RandomCenterPosition());

	gk->SetAuraColor(sf::Color::Yellow);
}

void GKHoldBall::Execute(GoalKeeper* gk)
{
	if (gk->GetTeam()->IsUserControlled())
	{
		gk->GetTeam()->SetPlayerUserControlled(gk);
		return;
	}
	
	if(!gk->IsWaiting())
	{
		if (!gk->GetTeam()->IsGkWithBall() 
			&& gk->AtHome() 
			&& gk->DistToNearestOpponent() < Params::GKSafenessRange)
		{
			gk->GetTeam()->GkControllBall();
			if (!gk->GetTeam()->IsUserControlled())
				gk->Wait(180);
			return;
		}

		BasePlayer* passTo = gk->FindPass();
		if( passTo )
		{
			gk->GetTeam()->GkLostBall();
			//if (gk->GetTeam()->IsGkWithBall())
			//	//passaggio "alto"? //FIX ME GK
			gk->Pass(passTo);
			gk->GetFSM()->ChangeState(GKIdle::Istance());
			return;
		}
		else
		{
			//rinvio pseudo-casuale
			float yDir;
			RandBetween(0,1)? 
				yDir = gk->GetTeam()->Game()->Pitch()->Top() : yDir = gk->GetTeam()->Game()->Pitch()->Bottom();
			gk->Shoot(Vector2(gk->GetTeam()->Game()->Pitch()->Center().x, yDir) - Ball::Istance()->GetPosition());
			gk->GetFSM()->ChangeState(GKIdle::Istance());
			gk->GetTeam()->GkLostBall();
			return;
		}
	}

	if (!gk->GetTeam()->IsGkWithBall() && gk->AtHome())
	{
		gk->GetTeam()->GkControllBall();
		gk->Wait(180);
	}
	
}

void GKHoldBall::Exit(GoalKeeper* gk)
{
	gk->Steering()->SeekOff();
}

bool GKHoldBall::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}


/*	GKCatchBall
	It's Singleton, to call it: GKGlobalState::Istance();
	Tha ball is incoming to the defending goal, it's gk duty try to catch it.
*/

void GKCatchBall::Enter(GoalKeeper* gk)
{
	gk->Steering()->SeekOn();
	gk->SetAuraColor(sf::Color::Magenta);
	gk->SetTarget(Ball::Istance()->GetPosition());
}

void GKCatchBall::Execute(GoalKeeper* gk)
{

		
	if( gk->GetTeam()->DistBallToHomeGoal()>=150.f  || (gk->GetTeam()->InControl() && Ball::Istance()->IsControlled()))
	{
		gk->GetFSM()->ChangeState(GKIdle::Istance());
		return;
	}
	else if(gk->DistToBall() <= Params::GKBallCaptureRange && Ball::Istance()->IsControlled())//uscita bassa
	{
		TelegramDispatcher::Istance()->DispatchTelegram( gk->ID(), 
															Ball::Istance()->GetOwner()->ID(),
															Tackled, 0.f );
		Ball::Istance()->CapturedBy(gk);
		gk->SetTarget(gk->GetPosition());
		gk->GetTeam()->SetControllingPlayer(gk);
		gk->GetTeam()->OpponentTeam()->LostControl();
		gk->GetTeam()->GkControllBall();
		gk->GetFSM()->ChangeState(GKHoldBall::Istance());
		return;

	}
	else if(gk->DistToBall() <= Params::GKBallCaptureRange && !Ball::Istance()->IsControlled() )
	{
		if((Ball::Istance()->GetSpeed()).SquaredLength() > 13 && RandBetween(0,1))//saving-repulsion
		{
			Vector2 bSpeed = Ball::Istance()->GetSpeed();
			Vector2 repulsion(-0.5f * bSpeed.x, 2.f * bSpeed.y);
			repulsion.Normalize();
			repulsion *= bSpeed.Length() * 0.5f;
			Ball::Istance()->Kick(repulsion);
			gk->SetTarget(gk->GetPosition());
			gk->Wait(30);
			gk->GetFSM()->ChangeState(GKIdle::Istance());
		}
		else//saving-keep in hands
		{
			Ball::Istance()->CapturedBy(gk);
			gk->SetTarget(gk->GetPosition());
			gk->GetTeam()->SetControllingPlayer(gk);
			gk->GetTeam()->OpponentTeam()->LostControl();
			gk->GetTeam()->GkControllBall();
			gk->Wait(100);
			gk->GetFSM()->ChangeState(GKHoldBall::Istance());
		}
		return;
	}		
	
	gk->SetTarget(Ball::Istance()->GetPosition());
}

void GKCatchBall::Exit(GoalKeeper* gk)
{
	gk->Steering()->SeekOff();
}

bool GKCatchBall::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}

/*	GKReceivePass
	It's Singleton, to call it: GKGlobalState::Istance();
	The gk is receiving a pass from a team's member
*/

void GKReceivePass::Enter(GoalKeeper* gk)
{
	gk->Steering()->PursuitOn();
	gk->SetAuraColor(sf::Color::Cyan);
	gk->SetTarget(gk->GetPosition());
}

void GKReceivePass::Execute(GoalKeeper* gk)
{
	//Ball Lost
	if (!gk->GetTeam()->InControl() || Ball::Istance()->IsControlled())
	{
		gk->GetFSM()->ChangeState(GKIdle::Istance());
		return;
	}

	//if Ball is in range, capture it.
	if ( (Ball::Istance()->GetPosition() - gk->GetPosition()).Length() < Params::GKBallCaptureRange )
	{
		Ball::Istance()->CapturedBy(gk);
		gk->SetTarget(gk->GetPosition());
		gk->GetTeam()->SetControllingPlayer(gk);
		gk->GetFSM()->ChangeState(GKHoldBall::Istance());
		return;
	}
}

void GKReceivePass::Exit(GoalKeeper* gk)
{
	gk->Steering()->PursuitOff();
}

bool GKReceivePass::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}

/*	GKIdle
	It's Singleton, to call it: GKIdle::Istance();
	It's the gk's main state. Just wait, interposing between ball and center of his goal. When Ball
	comes in the GKInterceptRange, gk moves to GKCatchBall state.
*/

void GKIdle::Enter(GoalKeeper* gk)
{
	gk->Steering()->InterposeOn();
	gk->SetAuraColor( sf::Color::White );
	gk->Steering()->SetInterposeFrom(gk->GetTeam()->HomeGoal()->Center());
}

void GKIdle::Execute(GoalKeeper* gk)
{
	if(!gk->IsWaiting())
	{
		gk->Steering()->InterposeOn();
		if ( gk->GetTeam()->DistBallToHomeGoal()<=150.f && 
			( !gk->GetTeam()->InControl() || (!Ball::Istance()->IsControlled() && !gk->IsControllingPlayer())))
		{
			gk->GetFSM()->ChangeState(GKCatchBall::Istance());
			return;
		}

		gk->Steering()->SetInterposeTo(Ball::Istance()->GetPosition());
		
		gk->CalculateInterposeWeight();
		gk->SetAuraColor( sf::Color::White );
	}
	else
	{
		gk->Steering()->InterposeOff();
		gk->SetAuraColor( sf::Color::Black );
	}
}

void GKIdle::Exit(GoalKeeper* gk)
{
	gk->Steering()->InterposeOff();
}

bool GKIdle::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}

/*	GKKickOff
	It's Singleton, to call it: GKKickOff::Istance();
	Prepare for KickOff
*/

void GKKickOff::Enter(GoalKeeper* gk)
{
	gk->Steering()->ArriveOn();
	gk->SetAuraColor( sf::Color::White );
	gk->Steering()->SetTarget(gk->GetHomeRegion()->RandomCenterPosition());
}

void GKKickOff::Execute(GoalKeeper* gk)
{
	if (gk->AtTarget() && gk->GetTeam()->Game()->IsOn())
	{	
		gk->GetFSM()->ChangeState(GKIdle::Istance());
	}
}

void GKKickOff::Exit(GoalKeeper* gk)
{
	gk->Steering()->ArriveOff();
}

bool GKKickOff::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}

/*	GKUserControlled
	It's Singleton, to call it: GKUserControlled::Istance();
	Gk has the ball. Come back to home if there isn't a possible pass.
*/

void GKUserControlled::Enter(GoalKeeper* gk)
{
	gk->Steering()->SeekOn();
	gk->SetAuraColor( sf::Color::Blue );
}

void GKUserControlled::Execute(GoalKeeper* gk)
{	
	if (gk->GetTeam()->GetPlayerUserControlled()!=gk)
	{
		gk->GetFSM()->ChangeState(GKIdle::Istance());
		return;
	}
	if (!gk->GetTeam()->IsGkWithBall() 
		&& gk->AtHome() 
		&& gk->DistToNearestOpponent() < Params::GKSafenessRange)
	{
		gk->GetTeam()->GkControllBall();
		return;
	}
	if (gk->IsControllingPlayer() && gk->GetTeam()->DistBallToHomeGoal() > 150)
	{
		gk->GetTeam()->GkLostBall();
	}
}

void GKUserControlled::Exit(GoalKeeper* gk)
{
	gk->Steering()->SeekOff();
}

bool GKUserControlled::OnMessage(GoalKeeper* gk, const Telegram & telegram)
{
	return false;
}

